/**
 * @class 网格绘制器
 * @author DuskyHuang 圣子
 * @description 提供网格和尺度相关的渲染逻辑。
*/

import { _decorator, renderer, MeshRenderer, Color, Vec3 } from 'cc';
import { Mediator, core } from 'cfw';
import { loadUnitAndColor, loadUnitAndColorI18n, setLabelStyle } from 'helpers';
import { constStars, cosmos, gizmos, GizmosProxy, planetary, ui, UIProxy, visit } from 'proxies';
import { CelestialLabel } from 'prefabs';
import { CosmosController } from '../controllers/cosmos-controller';
import { View } from 'notifications';
import { attenuate, smoothstep } from 'algorithms';
const { ccclass } = _decorator;

@ccclass('cocosmos.GridsRenderer')
export class GridsRenderer extends Mediator {

    // 颜色渐变
    public fade(out: boolean, duration: time.sec = 1): void {
        this._dAlpha = (out ? -1 : 1) / (Math.abs(duration) + Number.EPSILON);
    }

    protected onLoad(): void { this._init()._listen(); }

    protected lateUpdate(dt: time.sec): void {
        this._updateAlpha(dt);
        this._updatePosY();
        this._updatePow10();
        this._updateLabels();
    }

    private _init(): this {
        this._csc = core.getController(CosmosController);
        const mat = this.getComponent(MeshRenderer)?.material;
        this._pass0 = mat.passes[0];
        this._handlePow10 = this._pass0.getHandle('pow10');
        this._handleRatio = this._pass0.getHandle('ratio');
        this._handleGridColor = this._pass0.getHandle('gridColor');
        this._handleCircColor = this._pass0.getHandle('circColor');
        return this;
    }

    private _listen(): void {
        this.on(View.Transform.ZOOM_OVER, this._fadeOutIn);
    }

    private _fadeOutIn(): void {
        this.unscheduleAllCallbacks();
        this.fade(true, 1);
        this.scheduleOnce(() => this.fade(false, 2), 1);
    }

    private _updateAlpha(dt: time.sec): void {
        this._alpha += this._dAlpha * dt;
        if (this._alpha >= 1) { this._alpha = 1; this._dAlpha = 0; }
        else if (this._alpha <= 0) { this._alpha = this._dAlpha = 0; }
    }

    private _updatePosY(): void {
        if (cosmos.solar && visit.center && planetary.unbelt) {
            const y = visit.center?.node?.worldScale?.y ?? 0;
        } else this.node.setPosition(Vec3.ZERO);
    }

    private _createLabels(): void {
        Array.nature(3).forEach(() => this._steps.push(this._createLabel('pow10')));
    }

    private _createLabel(name: string, str: string = ''): CelestialLabel {
        const label = CelestialLabel.claim('ruler', name);
        label.camera = this._csc.mainCamera;
        label.target = this.node;
        label.string = str;
        setLabelStyle(label, 'ruler', 'unit');
        return label;
    }

    private _updatePow10(): void {
        const { pow10, } = cosmos, { showGrids, showRulers } = gizmos;
        const { inSolar: solar, inSatellite: satellite } = visit;
        if (solar) {
            switch (true) {
                case pow10 > LY_AU: // LY
                    this._ratio = RAT_LY;
                    lines.set(LIGHT_YEAR);
                    grids.set(GALACTIC); break;
                case pow10 > LY_AU - 1: // AU-LY
                    const t = 1 + pow10;
                    this._ratio = Math.lerp(RAT_AU, RAT_LY, t);
                    Color.lerp(lines, ASTRO_UNIT, LIGHT_YEAR, t);
                    Color.lerp(grids, SOLAR, GALACTIC, t); break;
                default: // AU
                    this._ratio = RAT_AU;
                    lines.set(ASTRO_UNIT);
                    grids.set(SOLAR); break;
            }
        } else {
            if (pow10 > AU_KM - 1) {
                const r = pow10 - (AU_KM - 1);
                this._ratio = Math.lerp(1, RAT_AU, r);
            } else this._ratio = 1;
            lines.set(satellite ? KILO_MT_DK : KILO_METER);
            grids.set(satellite ? SATELLITE : PLANETARY);
        }
        this._pass0.setUniform(this._handlePow10, pow10);
        this._pass0.setUniform(this._handleRatio, this._ratio);
        grids.a *= this._alpha;
        lines.a *= this._alpha;
        if (!showGrids) grids.set(Color.BLACK);
        if (!showRulers) lines.set(Color.BLACK);
        this._pass0.setUniform(this._handleGridColor, grids);
        this._pass0.setUniform(this._handleCircColor, lines);
    }

    private _updateLabels(): void {
        const show = gizmos.showRulers && ui.showNotes;
        if (show) {
            const { inSolar, inPlanetary } = visit;
            if (this._steps.length <= 0) this._createLabels();
            const { pow10 } = cosmos, iPow = Math.floor(pow10);
            const frac = pow10 - iPow;
            const nearest = visit.center?.nearest ?? -99;
            this._steps.forEach((label, i) => {
                const ignore = RULER_IGNORES.contains(iPow - i) || (iPow - i) < (nearest - 2);
                if (ignore) return label.alpha = 0;
                else {
                    const r = Math.pow(10, 1 - i - frac) * this._ratio;
                    // loadUnitAndColor(label, iPow - i + 1, inSolar, inPlanetary);
                    loadUnitAndColorI18n(label, iPow - i + 1, inSolar, inPlanetary);
                    this._relocateLabel(label, r, i - iPow, lines);
                }
            });
        } else {
            this._freeLabels();
        }
    }

    private _relocateLabel(label: CelestialLabel, r: number, rad: num.rad, arg: Color): CelestialLabel {
        const y = this.node.worldPosition.y;
        const att = attenuate(r, NameLabelAtt, false);
        label.alpha = att * this._alpha;
        Vec3.set(v3a, Math.sin(rad) * r, 0, Math.cos(rad) * r);
        label.setOffset(v3a);
        v3a.y = y;
        Color.copy(color, arg);
        color.a *= att;
        this._csc.mainRenderer.addFloatingDashLine(v3b.set(0, y, 0), v3a, color, 10, true, 0.75, 0.5);
        return label;
    }

    private _freeLabels(): void {
        this._steps.forEach(label => label.free());
        this._steps.length = 0;
    }

    private _csc: CosmosController;
    private _pass0: renderer.Pass;
    private _handlePow10: handle;
    private _handleRatio: handle;
    private _handleGridColor: handle;
    private _handleCircColor: handle;
    private _steps: CelestialLabel[] = [];
    private _alpha: number = 1;
    private _dAlpha: number = 0;
    private _ratio = 1;
}

const {
    LineColor: { KILO_MT_DK, KILO_METER, ASTRO_UNIT, LIGHT_YEAR },
    GridsColor: { GALACTIC, SOLAR, PLANETARY, SATELLITE }, RULER_IGNORES
} = GizmosProxy;
const { NameLabelAtt } = UIProxy;
const grids = new Color;   // 网格色
const lines = new Color;   // 标尺色
const AU_KM = -5;
const LY_AU = 0;
const RAT_AU = 1.496;
const RAT_LY = 0.946;

const color = new Color;
const v3a = new Vec3;
const v3b = new Vec3;